// Copyright (C) ZHENG Zhong <heavyzheng nospam-at gmail D0T com>
//
// $Id: document.hpp 210 2008-08-28 10:10:41Z heavyzheng $
//

#ifndef ZZHENG_XML_DOM_LIBXML2_DOCUMENT_HPP_20080717__
#define ZZHENG_XML_DOM_LIBXML2_DOCUMENT_HPP_20080717__

#include <zzheng/xml/config.hpp>
#include <zzheng/xml/dom/types.hpp>
#include <zzheng/xml/dom/basic_node_ptr.hpp>
#include <zzheng/xml/dom/node_proxy.hpp>
#include <zzheng/xml/dom/libxml2/base.hpp>
#include <zzheng/xml/dom/libxml2/libxml2_fwd.hpp>

#include <cassert>
#include <string>
#include <vector>

namespace zzheng {
namespace xml {
namespace dom {
namespace libxml2_ {

    ////////////////////////////////////////////////////////////////////////////////////////////////
    // forward declarations
    //

    class parser;
    class node;    
    class element;
    class attribute;
    class text;
    class comment;
    class xpath;

    ////////////////////////////////////////////////////////////////////////////////////////////////
    // document
    //

    //! This class represents an XML document.
    //! \todo TODO: re-consider friend classes.
    class document: private base {

        friend class node;    // TODO: node needs to access document's orphans.
        friend class element; // TODO: element needs to access document's orphans.
        friend class parser;  // parser needs private constructor.
        friend class xpath;   // TODO: xpath needs raw().

    public:

        //! Default constructor. Constructs an empty XML document.
        explicit document();

        //! Destroys the DOM tree and releases all resources.
        ~document();

        ////////////////////////////////////////////////////////////////////////////////////////////
        // property access
        //

        //! Returns the encoding of this document.
        //! \return the encoding of this document.
        std::string encoding() const;

        //! Returns the XML version number of this document.
        //! \return the XML version number of this document.
        std::string version() const;

        //! Checks if this document is empty. An empty document does not contain a root element.
        //! \return true if this document is empty, false otherwise.
        bool empty() const;

        //! Returns the root element of this document.
        //! \return the root element of this document.
        basic_node_ptr<element> root();

        //! Const version of root().
        basic_node_ptr<const element> root() const;

        //! Returns a string representation of the whole XML document.
        //! \return a string representation of the whole XML document.
        std::string str() const;

        //! Retrieves a string representation of the whole XML document.
        //! \param str  a string to hold the result.
        void str(std::string& str) const;

        //! Saves the whole XML document to a file.
        //! \param file_name  the file name.
        void save_to_file(const std::string& file_name) const;

        //document* clone(bool recursive) const;

        ////////////////////////////////////////////////////////////////////////////////////////////
        // modifiers
        //

        //! Clears the document. This function destroys the underlying DOM tree and releases all
        //! the resources, thus invalidates all the nodes (including orphan nodes).
        //! \post empty() == true
        void clear();

        //! Resets the root element to this document. This function will firstly clear the document
        //! then reset the root element.
        //! \post all the original nodes within this document are invalidated.
        //! \param elem  the element to reset as root.
        //! \return pointer to the new root element of this document.
        //! \throws bad_dom_operation  if $root is not an element.
        basic_node_ptr<element> reset_root(const element_proxy& elem);

        //! Resets the root element to this document. This function will firstly clear the document
        //! then reset the root element. This function makes a recursive clone of the element and
        //! resets the cloned one as the root element.
        //! \post all the original nodes within this document are invalidated.
        //! \param elem  the element to reset as root.
        //! \return pointer to the new root element of this document.
        basic_node_ptr<element> reset_root(const element& elem);

        //! Resets the root element to this document. This function will firstly clear the document
        //! then reset the root element. If adopt_node, this function takes the ownership of the
        //! element from the source DOM tree; if clone_node, this function clones the element and
        //! resets the cloned one as the root element, leaving the original element unchanged.
        //! \post all the original nodes within this document are invalidated.
        //! \param elem  the element to reset as root.
        //! \param aoc   adopt_node or clone_node, default to clone_node.
        //! \return pointer to the new root element of this document.
        //! \todo TODO: implement me!
        basic_node_ptr<element> reset_root(element& elem, adopt_or_clone_t aoc = clone_node);

        ////////////////////////////////////////////////////////////////////////////////////////////
        // node creation
        //

        basic_node_ptr<element> make_element(const std::string& name) const;

        basic_node_ptr<attribute> make_attribute(const std::string& name,
                                                 const std::string& value) const;

        basic_node_ptr<text> make_text(const std::string& value) const;

        basic_node_ptr<text> make_cdata(const std::string& value) const;

        basic_node_ptr<comment> make_comment(const std::string& value) const;

        ////////////////////////////////////////////////////////////////////////////////////////////
        // namespace prefix generation
        //

        //! Generates a namespace prefix which is guaranteed to be unique in the document scope.
        //! \return a unique namespace prefix.
        //! \todo TODO: how to generate document-scope unique namespace prefix?
        std::string next_namespace_prefix() const;

    private:

        ////////////////////////////////////////////////////////////////////////////////////////////
        // private
        //

        explicit document(xmlDoc* px);

        //! Non-implemented copy constructor.
        document(const document&);

        //! Non-implemented copy assignment.
        document& operator=(const document&);

        //! Returns the root element of this document, or null if this document is empty.
        //! \return the root element of this document, or null if this document is empty.
        const element* root_() const;

        //! Resets the libxml2 element node as the root element of this document.
        //! \post all the original nodes within this document are invalidated.
        //! \param px  the libxml2 element to reset as root, should not be null.
        //! \return pointer to the new root element of this document.
        basic_node_ptr<element> reset_root_(xmlNode* px);

        //! Checks if the specified node is an orphan of this document.
        //! \param orphan  the node to check.
        //! \return true if the specified node is an orphan of this document, false otherwise.
        bool is_my_orphan_(const node* orphan) const;

        void add_orphan_(node* orphan) const;

        void remove_orphan_(node* orphan) const;

        xmlDoc* raw() {
            assert(raw_ != 0);
            return raw_;
        }

        const xmlDoc* raw() const {
            assert(raw_ != 0);
            return raw_;
        }

    private:

        xmlDoc*                    raw_;     //!< The underlying libxml2 document.
        mutable int                counter_; //!< The counter used to generate namespace prefixes.
        mutable std::vector<node*> orphans_;

    }; // class document

} // namespace zzheng::xml::dom::libxml2_
} // namespace zzheng::xml::dom
} // namespace zzheng::xml
} // namespace zzheng


#endif // ZZHENG_XML_DOM_LIBXML2_DOCUMENT_HPP_20080717__



